/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.skatiger.jrove.spring.form.component;

import java.beans.PropertyEditor;

import org.skatiger.jrove.spring.tags.NestedPath;
import org.skatiger.jrove.spring.tags.SpringComponent;
import org.skatiger.jrove.spring.utils.ValueFormatter;
import org.springframework.beans.PropertyAccessor;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.support.BindStatus;
import org.springframework.web.servlet.tags.NestedPathTag;

public class HtmlFormComponent extends SpringComponent {
    private static final String DEFAULT_CSS_ERROR_CLASS = "valid_failed";
    // fields
    private String _path;
    private String _cssClass;
    private String _cssErrorClass;

    private BindStatus _bindStatus;// bind status

    // access method
    public void setPath(String path) {
        this._path = path;
    }

    public String getPath() {
        return getPropertyValue("path", _path);
    }

    // MEHOTD
    /**
     * Get the {@link BindStatus} for this tag.
     */
    public BindStatus getBindStatus() {
        if (this._bindStatus == null || this._path == null) {//if this.path is null, that use expression value
            String nestedPath = getNestedPath();
            String path = getPath();
            Assert.notNull(path);
            String pathToUse = (nestedPath != null ? nestedPath + path : path);
            if (pathToUse.endsWith(PropertyAccessor.NESTED_PROPERTY_SEPARATOR)) {
                pathToUse = pathToUse.substring(0, pathToUse.length() - 1);
            }
            this._bindStatus = new BindStatus(getRequestContext(), pathToUse,
                    false);
        }
        return this._bindStatus;
    }

    /**
     * Get the value of the nested path that may have been exposed by the
     * {@link NestedPathTag}.
     */
    protected String getNestedPath() {
        return (String) getViewContext().getAttribute(
                NestedPath.NESTED_PATH_VARIABLE_NAME);
    }

    /**
     * Get the bound value.
     * 
     * @see #getBindStatus()
     */
    public final Object getBoundValue() {
        return getBindStatus().getValue();
    }

    /**
     * Get the value for the HTML '<code>name</code>' attribute.
     * <p>
     * The default implementation simply delegates to {@link #getPropertyPath()}
     * to use the property path as the name. For the most part this is desirable
     * as it links with the server-side expectation for data binding. However,
     * some subclasses may wish to change the value of the '<code>name</code>'
     * attribute without changing the bind path.
     * 
     * @return the value for the HTML '<code>name</code>' attribute
     */
    public String getName() {
        return getPropertyPath();
    }

    /**
     * Get the {@link PropertyEditor}, if any, in use for value bound to this
     * tag.
     */
    public PropertyEditor getPropertyEditor() {
        return getBindStatus().getEditor();
    }

    /**
     * Build the property path for this tag, including the nested path but
     * <i>not</i> prefixed with the name of the form attribute.
     * 
     * @see #getNestedPath()
     * @see #getPath()
     */
    public String getPropertyPath() {
        String expression = getBindStatus().getExpression();
        return (expression != null ? expression : "");
    }

    /**
     * Gets the appropriate CSS class to use based on the state of the current
     * {@link org.springframework.web.servlet.support.BindStatus} object.
     */
    public String getStyleClass() {
        if (getBindStatus().isError()
                && StringUtils.hasText(getCssErrorClass())) {
            return ObjectUtils.getDisplayString(getCssErrorClass());
        } else {
            return ObjectUtils.getDisplayString(getCssClass());
        }
    }

    // css style class
    public void setCssClass(String _cssClass) {
        this._cssClass = _cssClass;
    }

    public String getCssClass() {
        return getPropertyValue("cssClass", _cssClass);
    }

    // css style error class
    public void setCssErrorClass(String _cssErrorClass) {
        this._cssErrorClass = _cssErrorClass;
    }

    public String getCssErrorClass() {
        return getPropertyValue("cssErrorClass", _cssErrorClass,
                DEFAULT_CSS_ERROR_CLASS);
    }

    /**
     * get form element display string
     * 
     * @return
     */
    public String getDisplayString() {
        return ValueFormatter.getDisplayString(getBoundValue(),
                getPropertyEditor(), isHtmlEscape());
    }
}
